package network;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

@SuppressWarnings({"all"})
public class TcpEchoServer {
    // listen -> 监听
    // 但是在 Java socket 中是体现不出来"监听"的含义的
    // 之所以这么叫，是操作系统原生的API 中有一个操作叫做listen
    private ServerSocket listenSocket = null;

    public TcpEchoServer(int port) throws IOException {
        listenSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动！");
        while (true) {
            // 由于 Tcp是有连接的，不能一上来就读取数据，而要先建立连接(接电话)
            // accept 就是在"接电话"，前提是有人给你打电话
            // accept 返回了一个socket 对象，称为clientSocket，后续和客户端的沟通都是通过clientSocket来完成的
            // 进一步讲，listenSocket就干了一件事，就是接电话
            Socket clientSocket = listenSocket.accept();
            processConnection(clientSocket);
        }
    }

    private void processConnection(Socket clientSocket) throws IOException {
        System.out.printf("[%s:%d] 客户端建立连接！", clientSocket.getInetAddress().toString(), clientSocket.getPort());
        // 接下来处理请求和响应
        // 这里的针对 TCP socket 的读写就和文件读写是一模一样的
        try (InputStream inputStream = clientSocket.getInputStream()) {
            try (OutputStream outputStream = clientSocket.getOutputStream()) {
                // 循环的处理每个请求，分别返回响应
                Scanner scanner = new Scanner(System.in);
                while (true) {
                    // 1. 读取请求
                    if (!scanner.hasNext()) {
                        System.out.printf("[%s:%d] 客户端断开连接！", clientSocket.getInetAddress().toString(), clientSocket.getPort());
                        break;
                    }
                    // 此处用 Scanner 更方便，如果不用Scanner 就用原生的InputStream的read
                    String request = scanner.next();
                    // 2. 根据请求，计算响应
                    String response = process(request);
                    // 3. 把这个响应返回客户端
                    // 为了方便响应，可以使用PrintWriter 把OutputStream包裹一下
                    PrintWriter printWriter = new PrintWriter(outputStream);
                    printWriter.println(response);
                    // 刷新缓冲区，如果没有这个刷新，可能客户端就不能第一时间看到响应
                    printWriter.flush();

                    System.out.printf("[%s:%d] req: %s, resp: %s\n", clientSocket.getInetAddress().toString(), clientSocket.getPort(), request, response);
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            clientSocket.close();
        }
    }

    private String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        TcpEchoServer server = new TcpEchoServer(9090);
        server.start();
    }
}
